package com.github.binarywang.demo.wx.mp.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.demo.wx.mp.builder.MessageDTO;
import com.github.binarywang.demo.wx.mp.builder.TextBuilder;
import com.github.binarywang.demo.wx.mp.service.CacheService;
import com.github.binarywang.demo.wx.mp.utils.OkHttpUtils;
import com.knuddels.jtokkit.api.Encoding;
import com.knuddels.jtokkit.api.EncodingRegistry;
import com.knuddels.jtokkit.api.ModelType;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Component
@AllArgsConstructor
public class MsgHandler extends AbstractHandler {

    private final Environment environment;

    private final CacheService cacheService;

    private final EncodingRegistry encodingRegistry;

    @Override
    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
                                    Map<String, Object> context, WxMpService weixinService,
                                    WxSessionManager sessionManager) {

//        if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
//            //TODO 可以选择将消息保存到本地
//        }
//
//        //当用户输入关键词如“你好”，“客服”等，并且有客服在线时，把消息转发给在线客服
//        try {
//            if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
//                && weixinService.getKefuService().kfOnlineList()
//                .getKfOnlineList().size() > 0) {
//                return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE()
//                    .fromUser(wxMessage.getToUser())
//                    .toUser(wxMessage.getFromUser()).build();
//            }
//        } catch (WxErrorException e) {
//            e.printStackTrace();
//        }
        //TODO 组装回复消息
        String format = "<a href=\"weixin://bizmsgmenu?msgmenucontent=%s&msgmenuid=1\">%s</a>\n";
        String chatgptUrl = environment.getProperty("api.chatgpt.url");
        if ("gpt".equals(wxMessage.getContent())) {
            // 开启gpt
            cacheService.start(wxMessage.getFromUser());
            return new TextBuilder().build("可以开始提问了，或者"+ String.format(format, "end", "关闭聊天"), wxMessage, weixinService);
        } else if ("end".equals(wxMessage.getContent())) {
            cacheService.clear(wxMessage.getFromUser());
            return new TextBuilder().build("已经清理上下文 " + String.format(format, "gpt", "开启聊天"), wxMessage, weixinService);
        }
        // 验证上下文
        List<MessageDTO> contextMessage = cacheService.getContextMessage(wxMessage.getFromUser());
        if (null == contextMessage) {
            return new TextBuilder().build("没有找到上下文哦 " + String.format(format, "gpt", "开启聊天"), wxMessage, weixinService);
        }
        // 缓存读取
        if (cacheService.msgCache(wxMessage.getMsgId()) != null) {
            String cacheContent = cacheService.msgRead(wxMessage.getMsgId());
            return new TextBuilder().build(cacheContent, wxMessage, weixinService);
        }
        if (wxMessage.getContent().startsWith("查询消息:")) {
            String msgId = wxMessage.getContent().replace("查询消息:", "");
            String cache = cacheService.msgCache(Long.parseLong(msgId));
            return new TextBuilder().build(cache == null ? "没有找到结果呢 请再等一会~" : cache, wxMessage, weixinService);
        }
        contextMessage.add(MessageDTO.builder()
                .role("user")
                .content(wxMessage.getContent()).build());
        // 计算长度
        Encoding encoding = encodingRegistry.getEncodingForModel(ModelType.GPT_3_5_TURBO);
        int total = 4090;
        int minCompletion = 200;
        int messageLength = 0;
        if (!CollectionUtils.isEmpty(contextMessage)) {
            // 不管
            for (MessageDTO messageDTO : contextMessage) {
                int tokens = encoding.countTokens(messageDTO.getContent());
                messageLength = messageLength + tokens + 8;
            }
        }
        logger.info("历史消息token:{}", messageLength);
        int fixCompletionLength = total - messageLength;
        if (fixCompletionLength < minCompletion) {
            if (contextMessage.size() == 1) {
                return new TextBuilder().build("提问内容过长，请精简问题。", wxMessage, weixinService);
            }
            // 消息删减
            if (contextMessage.size() < 3) {
                return new TextBuilder().build("上一对话内容过长,无法构建上下文语境,请" + String.format(format, "end", "停止聊天")
                        +"后重新开始聊天", wxMessage, weixinService);
            }
            // 从最开始删减
            Iterator<MessageDTO> iterator = contextMessage.iterator();
            while (iterator.hasNext()) {
                MessageDTO messageDTO = iterator.next();
                int tokens = encoding.countTokens(messageDTO.getContent());
                fixCompletionLength = fixCompletionLength + tokens + 8;
                iterator.remove();
                if (fixCompletionLength >= minCompletion) {
                    break;
                }
            }
        }
        final int maxToken = fixCompletionLength;
        long start = System.currentTimeMillis();
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            String content = getContent(chatgptUrl, contextMessage, maxToken);
            logger.info("请求返回内容:{}", content);
            if (!content.startsWith("请求失败")){
                JSONObject res = JSON.parseObject(content);
                if (res.get("error") != null) {
                    return "出错了哦，" + res.getJSONObject("error").get("message") + String.format(format, wxMessage.getContent(), "重新发送");
                }
                MessageDTO messageDTO = res
                        .getJSONArray("choices")
                        .getJSONObject(0)
                        .getObject("message", MessageDTO.class);
                // 将新的内容添加进缓存
                contextMessage.add(messageDTO);
                cacheService.put(wxMessage.getFromUser(), contextMessage);
                content = messageDTO.getContent();
            }else {
                content = content + String.format(format, wxMessage.getContent(), "重新发送");
            }
            cacheService.msgPut(wxMessage.getMsgId(), content);
            return content;
        });
        long limit = start + 4000;
        String content = "正在加载中,请稍后" + String.format(format, "查询消息:" + wxMessage.getMsgId(), "重新获取结果");
        while (System.currentTimeMillis() <= limit) {
            if (future.isDone()) {
                try {
                    content = future.get();
                } catch (ExecutionException | InterruptedException e) {
                    throw new RuntimeException(e);
                }
                break;
            }
        }
        String cache = cacheService.msgCache(wxMessage.getMsgId());
        if (cache == null) {
            // 第一次
            cacheService.msgPut(wxMessage.getMsgId(), content);
        }else {
            content = cache;
        }
        logger.info("content:{}", content);
        return new TextBuilder().build(content, wxMessage, weixinService);
    }

    private String getContent(String chatgptUrl, List<MessageDTO> contextMessage, int maxToken) {
        String apikey = environment.getProperty("api.chatgpt.apikey");
        return OkHttpUtils
                .builder()
                .url(chatgptUrl)
                .addHeader("authorization", "Bearer " + apikey)
                .addParam("model", "gpt-3.5-turbo")
                .addParam("messages", contextMessage)
                .addParam("max_tokens", maxToken)
                .post(true)
                .sync();
    }
}
